﻿using System;
using System.Net;
using System.Net.Sockets;

namespace NetworkLibrary
{
    public class TcpConnect
    {
        private Socket _socket;
        public Action _deconnect;
        private bool _isDeconnect;
        private SocketAsyncEventArgs _readEventArgs;
        private SocketAsyncEventArgs _writeEventArgs;
        private ISynchronizationContext _synchronizationContext;
        public TcpConnect(ushort id)
        {
            Id = id;
        }
        public ushort Id { get; }
        public string Host { get; private set; }
        public int Port { get; private set; }
        public void Start(Socket socket, IPEndPoint endPoint, Action deconnect)
        {
            _socket = socket;
            socket.NoDelay = true;
            Host = endPoint.Address.ToString();
            Port = endPoint.Port;
            _readEventArgs = new SocketAsyncEventArgs();
            _writeEventArgs = new SocketAsyncEventArgs();
            _readEventArgs.Completed += OnProcessReceive;
            _writeEventArgs.Completed += OnProcessSend;
            if (!socket.ReceiveAsync(readEventArgs))
            {
                OnProcessReceive(null, readEventArgs);
            }
            _deconnect = deconnect;
            _deconnect += () =>
            {
                readEventArgs.Completed -= OnProcessReceive;
                writeEventArgs.Completed -= OnProcessSend;
            };
        }
        private void OnEventArgsComplete(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    _synchronizationContext.SyncCall(() => OnConnectComplete(e));
                    break;
                case SocketAsyncOperation.Receive:
                    _synchronizationContext.SyncCall(() => OnRecvComplete(e));
                    break;
                case SocketAsyncOperation.Send:
                    _synchronizationContext.SyncCall(() => OnSendComplete(e));
                    break;
                case SocketAsyncOperation.Disconnect:
                    _synchronizationContext.SyncCall(() => OnDisconnectComplete(e));
                    break;
                default:
                    throw new Exception($"socket error: {e.LastOperation}");
            }
        }

        public Socket RemoteSock;
        private void ParseMessage(byte[] buffer)
        {

        }
        private void Deconnect()
        {
            if (_isDeconnect)
                return;
            _isDeconnect = true;
            _deconnect();
        }
        private void OnProcessReceive(object sender, SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                e.Buffer;
                Console.WriteLine("The server has read a total of {0} bytes", _totalBytesRead);

                //echo the data received back to the client
                e.SetBuffer(e.Offset, e.BytesTransferred);
                bool willRaiseEvent = token.Socket.SendAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessSend(e);
                }
            }
            else
            {
                _deconnect();
            }
        }
        private void OnProcessSend(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                // read the next block of data send from the client
                bool willRaiseEvent = _socket.ReceiveAsync(e);
                if (!willRaiseEvent)
                {
                    OnProcessSend(e);
                }
            }
            else
            {
                _deconnect();
            }
        }
        internal void OnReadWriteCompleted(object sender, SocketAsyncEventArgs e)
        {
            // determine which type of operation just completed and call the associated handler
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    OnProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    OnProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }
    }
}
